package com.jun.common.redis.aspect;

import com.jun.common.redis.aspect.anno.RedisLock;
import lombok.Data;
import lombok.SneakyThrows;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.assertj.core.util.Lists;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author yuejun.li
 * @date 2024/7/21 13:50
 */
@Aspect
@Component
public class RedisLockAspect {

    @Autowired
    private RedissonClient redissonClient;

    @Around("@annotation(redisLock)")
    public Object around(ProceedingJoinPoint point, RedisLock redisLock) {
        try {
            if ((redisLock.leaseTime() > 0 || redisLock.leaseTime() == -1) && redisLock.waitTime() >= 0) {
                String key = this.getLockKey(point, redisLock);
                RLock lock = redissonClient.getLock(key);
                boolean getLock = false;
                Object proceed;

                try {
                    getLock = lock.tryLock((long)redisLock.waitTime(), (long)redisLock.leaseTime(), redisLock.unit());
                    if (!getLock) {
                        throw new RuntimeException("系统繁忙,请稍后再试");
                    }
                    proceed = point.proceed();
                } catch (Exception e) {
                    throw e;
                }finally {
                    if (lock != null && lock.isHeldByCurrentThread()) {
                        lock.unlock();
                    }
                }
                return proceed;
            }
            else {
                throw new RuntimeException("锁参数无效");
            }
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取key
     *
     * @param point
     * @param redisLock
     * @return
     */
    private String getLockKey(ProceedingJoinPoint point, RedisLock redisLock) {
        //方法的参数
        Object[] args = point.getArgs();

        List<OrderParameter> orderParameters = Lists.newArrayList();

        MethodSignature methodSignature = (MethodSignature) point.getSignature();
        Method method = methodSignature.getMethod();
        //行表示拥有注解的参数的个数，列表示一个参数拥有注解的个数
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();

        for (int i = 0; i < parameterAnnotations.length; ++i) {
            Object arg = args[i];
            Annotation[] parameterAnnotation = parameterAnnotations[i];
            if (parameterAnnotation.length != 0) {
                Arrays.stream(parameterAnnotation)
                        .filter(x -> x instanceof RedisLock.KeyParam)
                        .findFirst()
                        .ifPresent(x -> this.keyParamWork(x, arg, orderParameters));
            }
            Arrays.stream(parameterAnnotation)
                    .filter(x -> x instanceof RedisLock.KeyParamBean)
                    .findFirst()
                    .ifPresent(x -> this.keyParamBeanWork(arg, orderParameters));
        }
        orderParameters.sort(Comparator.comparing(OrderParameter::getOrder));
        int count = (int) orderParameters.stream().map(OrderParameter::getOrder).distinct().count();
        if (count != orderParameters.size()) {
            throw new RuntimeException("order顺序不能重复");
        }

        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(redisLock.value());

        for (OrderParameter parameter : orderParameters) {
            stringBuffer.append(":").append(parameter.getParameter());
        }
        return stringBuffer.toString();
    }

    private void keyParamBeanWork(Object arg, List<OrderParameter> orderParameters) {
        try {
            if (arg == null) {
                throw new RuntimeException("Bean对象不能没有字段或字段值不能为null");
            }
            Field[] fields = arg.getClass().getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                //该字段有@RedisLock.KeyParam修饰
                RedisLock.KeyParam annotation = field.getAnnotation(RedisLock.KeyParam.class);
                if(annotation != null){
                    this.keyParamWork(annotation, field.get(arg), orderParameters);
                }
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
    }

    private void keyParamWork(Annotation annotation, Object arg, List<OrderParameter> keyParams) {
        if (arg == null) {
            throw new RuntimeException("key不能为null");
        }
        RedisLock.KeyParam keyParam = AnnotationUtils.getAnnotation(annotation, RedisLock.KeyParam.class);
        if (keyParam != null) {
            OrderParameter orderParameter = new OrderParameter();
            orderParameter.setParameter(String.valueOf(arg));
            orderParameter.setOrder(keyParam.order());
            keyParams.add(orderParameter);
        }
    }

    @Data
    public static class OrderParameter {

        private int order;

        private String parameter;

    }
}